package sk.tuke.magsa.tools.parser;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
//import java.util.stream.Stream;
import javax.swing.text.Document;
//import jdk.jfr.events.FileReadEvent;
import sk.tuke.magsa.tools.metamodel.Entity;
import sk.tuke.magsa.tools.metamodel.Model;
import sk.tuke.magsa.tools.metamodel.Property;
import sk.tuke.magsa.tools.metamodel.Type;

public class LineParser {
    private static final String SEPARATOR = ":";
    private int lineNumber = 0;

    public Model parseDir(File dir) throws ParserException {
        if (!dir.isDirectory()) {
            throw new ParserException(dir + " is not a directory");
        }

        List<Entity> entities = new ArrayList<Entity>();
        for (File file : dir.listFiles()) {
            entities.add(parse(file));
        }
        return new Model(entities.toArray(new Entity[]{}));
    }

    public Entity parse(File file) throws ParserException {
        try {
            Reader reader = new FileReader(file);
            String name = file.getName();
            name = name.substring(0, name.lastIndexOf('.'));
            return parse(name, reader);
        } catch (IOException e) {
            throw new ParserException("Cannot open file " + file, e);
        }
    }

    private Entity parse(String name, Reader reader) throws ParserException, IOException{
        BufferedReader bf = new BufferedReader(reader);
        Entity entity;
        //List<Property> propsList = new ArrayList<Property>();// just for counting properties before creating instance of Property class
        Property props[];
        String line;
        HashSet<Property> hSet = new HashSet();
        
        
        line = bf.readLine().trim();
        lineNumber++;
        while(line != null)
        {
            while(line.startsWith("#"))
            {
                line = bf.readLine();
                lineNumber++;
            }
            Property p = parseLine(line.trim());
            
            for(Property o : hSet)
            {
                if(o.getName().equals(p.getName()))
                    throw new ParserException("Variable " + p.getName() + " already exists in entity. Error occurs on " + lineNumber + ". line");
            }
            hSet.add(p);
            
            line = bf.readLine();
            lineNumber++;
        }
        //int num = propsList.size();
        int num = hSet.size();
        props = new Property[num];
        
        num = 0;
        for(Object p : hSet)
        {
           props[num++] =  (Property)p; 
       }
        
        entity = new Entity(name, props);        
        
        return entity;
    }

     private Property parseLine(String line) throws ParserException {
        String[] variable = line.split(SEPARATOR);
        if(variable.length > 1)
        {
            Type type = GetType(variable[1].trim());
            IsValid(variable[0].trim());
            return new Property(variable[0].trim(), type);
        }
        else
        {
            IsValid(line);
            return new Property(line, Type.STRING);
        }
    }

    private void IsValid(String line) throws ParserException {
        if(!line.matches("[a-zA-Z_$]\\w*"))
            throw new ParserException("Variable is not valid according to Java rules. Error occurs on " + lineNumber + ". line");
    }

    private Type GetType(String type) {
        switch(type)
        {
            case "integer": return Type.INTEGER;
            case "real": return Type.REAL;
            default: return Type.STRING;
        }
    }
}
